#ifndef DRIVER_MOUSE_H
#define DRIVER_MOUSE_H

#include <os/driver.h>
#include <lib/type.h>

#define DRIVER_NAME "input-mouse"
#define DEVICE_NAME "mouse"
#define DRIVER_VERSION "0.1"

// define ps2 controller io port
#define PS2CTRL_IO_DATA_PORT 0X60
#define PS2CTRL_IO_STATUS_PORT 0X64
#define PS2CTRL_IO_CMD_PORT 0X64

// define command filed
// read first byte from internal ram
#define PS2CTRL_CMD_READCONFIGBYTE 0X20
// read n bytes from internal ram
#define PS2CTRL_CMD_READCONFIGNBYTE(n) (n - 1) + PS2CTRL_CMD_READBYTE
// write next byte to first byte of internal ram
#define PS2CTRL_CMD_WRITECONFIGBYTE 0x60
// write next byte to n bytes of internal ram
#define PS2CTRL_CMD_WRITECONFIGNBYTE(n) (n - 1) + PS2CTRL_CMD_WRITEBYTE
// disable second PS/2 port
#define PS2CTRL_CMD_DISSECONDPORT 0XA7
// able second PS/2 port
#define PS2CTRL_CMD_ENSECONDPORT 0xA8
// test second PS/2 port
#define PS2CTRL_CMD_TESTSECONDPORT 0XA9
// test PS/2 controller
#define PS2CTRL_CMD_TESTCONTROLLOER 0XAA
// test first PS/2 port
#define PS2CTRL_CMD_TESTFIRSTPORT 0XAB
// diagnostic dump
#define PS2CTRL_CMD_DGNSTCDUMP 0XAC
// disable first PS/2 port
#define PS2CTRL_CMD_DISFIRSTPORT 0XAD
// able first PS/2 port
#define PS2CTRL_CMD_ENFIRSTPORT 0XAE
// read controller input port
#define PS2CTRL_CMD_RDCTRLIN 0XC0
// copy bits 0 to 3 of input port to status bits 4 to 7
#define PS2CTRL_CMD_CPINTOSTU1 0XC1
// copy bits 4 to 7 of input port to status bits 4 to 7
#define PS2CTRL_CMD_CPINTOSTU2 0XC2
// read controller outport port
#define PS2CTRL_CMD_RDCTRLOUT 0xD0
// write next byte to controller output port
#define PS2CTRL_CMD_WRTNEXTBYTE 0XD1
// write next byte to first PS/2 port output buffer
#define PS2CTRL_CMD_WRITENEXTBYTE_TO_FIRSTPOSTOUTBUFF 0XD2
// write next byte to second PS/2 port output buffer
#define PS2CTRL_CMD_WRITENEXTBYTE_TO_SECONDPORTOUTBUF 0XD3
// write next byte to second PS/2 port input buffer
#define PS2CTRL_CMD_WRITENEXTBYTE_TO_SECONDPORTINBUFF 0XD4
// pulse output line low for 6 ms
#define PS2CTRL_CMD_ENFULLPULSELINE 0xF0
// disable 0 pulse line
#define PS2CTRL_CMD_DISPULSELINE0 PS2CTRL_CMD_ENFULLPULSELINE | 0X01
// disable 1 pulse line
#define PS2CTRL_CMD_DISPULSELINE1 PS2CTRL_CMD_ENFULLPULSELINE | 0X02
// disable 2 pulse line
#define PS2CTRL_CMD_DISPULSELINE2 PS2CTRL_CMD_ENFULLPULSELINE | 0X04
// disable 3 pulse line
#define PS2CTRL_CMD_DISPULSELINE3 PS2CTRL_CMD_ENFULLPULSELINE | 0X08
// cpu reset
#define PS2CTLR_CMD_CPURESET 0xFE

// disable device scan
#define PS2_DEVICE_CMD_DISSCAN 0xF5
// enable device scan
#define PS2_DEVICE_CMD_ENSCAN 0xF4
// get device identify
#define PS2_DEVICE_CMD_IDENTIFY 0xF2
// device respond
#define PS2_DEVICE_CMD_RETURN_ACK 0xFA

// define PS/2 configuration filed mask
// enable first ps/2 port interrupt
#define PS2CTRL_CONFIG_FIRSTPORTINT 0x1
// enable second ps/2 port interrupt
#define PS2CTRL_CONFIG_SECONDPORTINT 0X2
// system flag bits if system passed POST,set
#define PS2CTRL_CONFIG_SYSTESTOK 0X4
// zero
#define PS2CTRL_CONFIG_ZERO0 0x8
// first ps/2 port clock
#define PS2CTRL_CONFIG_FIRSTPORTCLOCK 0x10
// second ps/2 port clock
#define PS2CTRL_CONFIG_SECONDPORTCLOCK 0x20
// translation second scancode to first scancode
#define PS2CTRL_CONFIG_SCANCODETRANSLATION 0x40
// zero
#define PS2CTRL_CONFIG_ZERO1 0X80

// ps2 controller status
#define PS2CTRL_STATUS_OUTFULL 0X01
#define PS2CTRL_STATUS_INFULL 0X2
#define PS2CTRL_STATUS_SYSFLAG 0X4
#define PS2CTRL_STATUS_CMD_DATA 0X8
#define PS2CTRL_STATUS_UNKNOW0 0x10
#define PS2CTRL_STATUS_WHICHBUFF 0x20
#define PS2CTRL_STATUS_MOUSEBUFF 0x20
#define PS2CTRL_STATUS_KEYBOARDBUFF 0x00
#define PS2CTRL_STATUS_TIMOUTERR 0x40
#define PS2CTRL_STATUS_PARITYERR 0X80

// ps2 device type
#define PS2_TYPE_MOUSE 0    // ps2 mouse
#define PS2_TYPE_KEYBOARD 1 // ps2 keyboard
#define PS2_TYPE_NONE 2     // ps2 unknow device

// ps2 controller config
#define PS2_CONFIG (PS2CTRL_CONFIG_FIRSTPORTINT | PS2CTRL_CONFIG_SECONDPORTINT | PS2CTRL_CONFIG_SCANCODETRANSLATION)

// mouse data mask
#define MOUSE_DATA_BUTTON_MASK 0x07

typedef struct
{
    // y overflow|x overflow|y sign bit|x sign bit
    // always 1|middle btn|right btn|left btn
    uint8_t bytes0;
    // x movement
    uint8_t bytes1;
    // y movement
    uint8_t bytes2;
    // z movement
    uint8_t bytes3;
    // always0|always0|5th btn|4th bth
    // horizontal scroll left|horizontal scroll right|vertical scroll down|vertical scroll up
    uint8_t bytes4;
} mouse_data_t;

typedef struct
{
    device_object_t *devobj;
    device_handle_t mouse;

    uint8_t irq;
    mouse_data_t mouse_data;
    uint8_t phase;
    uint8_t raw_data[5];          // raw data
    input_event_buff_t eventbuff; // event buffer

    uint8_t has_wheel; // mouse wheel
    uint8_t open;      // device open

    uint8_t type; // type
    uint8_t id;   // port

    uint8_t exist;

    uint8_t button;
    int seq;
    flags_t flags;
} device_extension_t;

enum mouse_return_code
{
    MOUSE_RET_ACK = 0xFA,
};

enum mouse_type
{
    MOUSE_INTELLI = 0x03
};

enum mouse_cmd
{
    MOUSE_CMD_RESET = 0xFF,          // mouse reset
    MOUSE_CMD_SEND = 0xFE,           // send data
    MOUSE_CMD_DISABLE_SEND = 0xF5,   // disable autosend
    MOUSE_CMD_ENABLE_SEND = 0xF4,    // enable autosend
    MOUSE_CMD_GETID = 0xF2,          // get mouse id
    MOUSE_CMD_SETDEFALUT = 0xF6,     // set defalut
    MOUSE_CMD_SET_RATE = 0xF3,       // set rate
    MOUSE_CMD_REQUEST_PACKED = 0xEB, // request single packed
};

#endif